/*
    p347 test fpga driver
    
    uses GPMC, SPI, GPIO
    uses _PDB instead of printk for detailed information
*/
#include <linux/p347_fpga.h>

#define GPMC_CS0		0x60
#define GPMC_CS_SIZE		0x30

static void* d_ret = NULL;

static p347_fpga_device_t	p347_fpga_info;

//static struct gpmc_timings g_time;
static unsigned long fpga_mem_read_base;
static unsigned long fpga_mem_write_base;
static unsigned long fpga_mem_read_addr;
static unsigned long fpga_mem_write_addr;

static void __iomem *gpmc_base;// = 0x6e000000;
static struct clk *gpmc_l3_clk = NULL;
static struct clk *dpll3_clk = NULL;

static spinlock_t irq_lock;
static const unsigned short GPMC_MHZ = 80;

static t_p347_inner	tpi;

int _create_buffer(t_data_buffer *dbuf) {
    if (!dbuf) return -EINVAL;
    memset(dbuf,0,sizeof(t_data_buffer));
    dbuf->ptr = kcalloc(p347_PUMPBUF_FULLSIZE,sizeof(u16),GFP_KERNEL);
    if (!dbuf->ptr) {
	printk("ERROR: cannot allocate buffer for DATAPUMP\n");
	return -ENOMEM;
    }
    spin_lock_init(&(dbuf->lock));
    
    _PDBA("!!!!!!!!!!!!!!!!! wpos=%d, rpos=%d, cur_len=%d\n",
	atomic_read(&(dbuf->write_pos)),atomic_read(&(dbuf->read_pos)),atomic_read(&(dbuf->cur_len)) );
    
    return 0;
}

void _delete_buffer(t_data_buffer *dbuf) {
    if (!dbuf) return;
    if (dbuf->ptr) {
	kfree(dbuf->ptr);
    }
    memset(dbuf,0,sizeof(t_data_buffer));
}

//==========================================================================
//==========================================================================		GPMC
//==========================================================================

u32 my_gpmc_cs_read_reg(int cs, int idx)
{
	void __iomem *reg_addr;

	reg_addr = gpmc_base + GPMC_CS0 + (cs * GPMC_CS_SIZE) + idx;
	return __raw_readl(reg_addr);
}

void my_gpmc_cs_write_reg(int cs, int idx, u32 val)
{
    void __iomem *reg_addr;

    reg_addr = gpmc_base + GPMC_CS0 + (cs * GPMC_CS_SIZE) + idx;
    __raw_writel(val, reg_addr);
}

//==========================================================================
//==========================================================================		GPMC
//==========================================================================

#define TRY_SET(_idx_,_val_) \
    ret = spi_write_adc_register(adc_idx,_idx_,_val_);\
    if (ret != 0) return ret

#define TRY_SET_FPGA(_idx_,_val_)\
    ret = spi_write_fpga_register(_idx_,_val_);\
    if (ret != 0) return ret

#define IS_CH_RUNNING(_idx_)\
    (atomic_read(&(tpi.ABUF[_idx_].is_running)))

static irqreturn_t p347_fpga_irq_handler(int irq, void* data)
{
    unsigned long flags;
    spin_lock_irqsave(&irq_lock,flags);

    p347_fpga_info.i_cnt++; //count interrupts
    //_PDBA("p347_fpga_irq_handler irq=%d\n",irq);
    
    omap_start_dma(p347_fpga_info.dma_ch);
    spin_unlock_irqrestore(&irq_lock,flags);

    return IRQ_HANDLED;
}

inline unsigned long calc_crc_32(void) {
    return ( ((unsigned short*)(d_ret))[0]+((unsigned short*)(d_ret))[1]+
	     ((unsigned short*)(d_ret))[2]+((unsigned short*)(d_ret))[3]+((unsigned short*)(d_ret))[4]);
}

void DISPATCH_64(void) {
    int i,tmpi,j;
    t_data_buffer* _abuf_;
    unsigned short* tptr;
    unsigned long jif2;
    
    for (i=0; i<p347_ADC_CHANNELS_CNT; i++) 
	if (IS_CH_RUNNING(i)) {
    	    _abuf_ = &(tpi.ABUF[i]);
    	    if ((atomic_read(&(_abuf_->cur_len)) + 2*DATA_FRAME_SIZE) > p347_PUMPBUF_FULLSIZE) {
		//TODO: overflow
		//_PDBA("Channel %d overflow\n",i+1);
		continue;
	    }
	    
	    atomic_add(2*DATA_FRAME_SIZE,&(_abuf_->cur_len));
	    tmpi = atomic_read(&(_abuf_->write_pos));
	    
	    spin_lock_irqsave(&(_abuf_->lock),jif2);
	    tptr = (unsigned short*)(_abuf_->ptr + tmpi*sizeof(u16));
            for (j=0; j<DATA_FRAME_SIZE; j++) { //copy proper channel words from 8-words pack
        	*tptr = ((unsigned short*)d_ret)[8*j+2*i];
        	tptr++;
        	*tptr = ((unsigned short*)d_ret)[8*j+2*i+1];
        	tptr++;
            }
	    spin_unlock_irqrestore(&(_abuf_->lock),jif2);
	    
	    tmpi += 2*DATA_FRAME_SIZE;
	    if (tmpi >= p347_PUMPBUF_FULLSIZE) tmpi-=p347_PUMPBUF_FULLSIZE;
	    atomic_set(&(_abuf_->write_pos),tmpi);
	    
	    //_PDBA("Pack complete for channel %d\n",i+1);
	} else { //debug
	    //_PDBA("DMA complete, but channel %d is not running\n",i+1);
	}
}

static void p347_fpga_dma_callback(int lch, u16 ch_status, void* data)
{
#ifdef CS67_MODE
    gpio_set_value(p347_GPIO_OSCDMA,0);
#endif

    DISPATCH_64();

    omap_stop_dma(p347_fpga_info.dma_ch);

#ifdef CS67_MODE
    gpio_set_value(p347_GPIO_OSCDMA,1);
#endif
}

//==========================================================================
//==========================================================================		COMMON
//==========================================================================

static int p347_fpga_open(struct inode *inode, struct file *filp)
{
    _PDBA("p347_fpga_open: inode=0x%p, filp=0x%p \n",(void*)inode,(void*)filp);

    if (p347_fpga_info.opened > 0) {
	printk("ERROR: device is already opened!\n");
	return -EBUSY;
    }
    p347_fpga_info.opened=1;
    //filp->private_data = &p347_fpga_info;

    //printk("FPGA_RESET=>1\n");
    //gpio_set_value(p347_GPIO_FPGA_RESET,1);

    return nonseekable_open(inode,filp);
}

//==========================================================================

static int p347_fpga_release(struct inode *inode, struct file *filp)
{
    _PDBA("p347_fpga_release: inode=0x%p, filp=0x%p \n",(void*)inode,(void*)filp);
    p347_fpga_info.opened=0;

    //printk("FPGA_RESET=>0\n");
    //gpio_set_value(p347_GPIO_FPGA_RESET,0);

    return 0;
}

//==========================================================================
//==========================================================================		SPI
//==========================================================================
//Basic SPI logic functions, may be called from ioctl or from inner logic process
// (NOT AT THE SAME TIME FROM BOTH!)

//adc_idx 1..4, reg_idx 1..N
int spi_read_adc_register(unsigned char adc_idx, unsigned char reg_idx, unsigned short* reg_val)
{
    int ret = 0;
    unsigned long	value = 0; //read

    p347_fpga_info.spi_tx_buff[0] = (adc_idx << 24) | (reg_idx << 16);
    p347_fpga_info.spi_tx_buff[1] = 0;
    p347_fpga_info.spi_tx_buff[2] = 0;
    //p347_fpga_info.spi_tx_buff[3] = 0;
    //p347_fpga_info.spi_tx_buff[4] = 0;
    memset(&p347_fpga_info.spi_rx_buff[0],0,SPI_RXBUFF_LEN*sizeof(u32));

    spi_message_init(&p347_fpga_info.msg);
    //p347_fpga_info.transfer.len = 5*sizeof(u32); //5 words (cmd,wait,wait,wait,get)
    p347_fpga_info.transfer.len = 3*sizeof(u32);
    p347_fpga_info.transfer.cs_change = 1;
    p347_fpga_info.transfer.bits_per_word = 32;
    p347_fpga_info.transfer.speed_hz = p347_fpga_info.new_hz;
    p347_fpga_info.transfer.tx_buf = p347_fpga_info.spi_tx_buff;
    p347_fpga_info.transfer.rx_buf = p347_fpga_info.spi_rx_buff;

    spi_message_add_tail(&p347_fpga_info.transfer,&p347_fpga_info.msg);

    //sleep until complete
    //TODO: test, think about hanging up, do something
    ret = spi_sync(p347_fpga_info.spidev,&p347_fpga_info.msg);
    //_PDBA("spi_sync in spi_read_adc_register ret=%d\n",ret);
    if (ret != 0) return ret;

    //*reg_val = (p347_fpga_info.spi_rx_buff[4] & 0x0000FFFF);
    *reg_val = (p347_fpga_info.spi_rx_buff[2] & 0x0000FFFF);
    _PDBA("Reg value = 0x%04x\n",*reg_val);

    return 0;
}

int spi_read_fpga_register(unsigned char reg_idx, unsigned short* reg_val)
{
    int ret = 0;

    p347_fpga_info.spi_tx_buff[0] = (0x5<<24) | (reg_idx << 16);
    p347_fpga_info.spi_tx_buff[1] = 0;
    p347_fpga_info.spi_tx_buff[2] = 0;
    memset(&p347_fpga_info.spi_rx_buff[0],0,SPI_RXBUFF_LEN*sizeof(u32));

    spi_message_init(&p347_fpga_info.msg);

    p347_fpga_info.transfer.len = 3*sizeof(u32); //3 words (cmd,wait,get)
    p347_fpga_info.transfer.cs_change = 1;
    p347_fpga_info.transfer.bits_per_word = 32;
    p347_fpga_info.transfer.speed_hz = p347_fpga_info.new_hz;
    p347_fpga_info.transfer.tx_buf = p347_fpga_info.spi_tx_buff;
    p347_fpga_info.transfer.rx_buf = p347_fpga_info.spi_rx_buff;

    spi_message_add_tail(&p347_fpga_info.transfer,&p347_fpga_info.msg);

    //sleep until complete
    //TODO: test, think about hanging up, do something
    ret = spi_sync(p347_fpga_info.spidev,&p347_fpga_info.msg);
    //_PDBA("spi_sync in spi_read_fpga_register ret=%d\n",ret);
    if (ret != 0) return ret;

    *reg_val = (p347_fpga_info.spi_rx_buff[2] & 0x0000FFFF);
    _PDBA("Reg value = 0x%04x\n",*reg_val);
    //_PDBA("rec[0]=0x%8x, rec[1]=0x%8x, reg_val=0x%4x\n",p347_fpga_info.spi_rx_buff[0],p347_fpga_info.spi_rx_buff[1],*reg_val);

    return 0;
}

int spi_write_adc_register(unsigned char adc_idx, unsigned char reg_idx, unsigned short reg_val)
{
    int ret;
    //request register writing
    p347_fpga_info.spi_tx_buff[0] = (1<<28) | (adc_idx<<24) | (reg_idx<<16) | (reg_val);
    p347_fpga_info.spi_tx_buff[1] = 0; //wait
    //p347_fpga_info.spi_tx_buff[2] = 0;
    //p347_fpga_info.spi_tx_buff[3] = 0;

    spi_message_init(&p347_fpga_info.msg);

    p347_fpga_info.transfer.len = 2*sizeof(u32); //length in bytes
    p347_fpga_info.transfer.cs_change = 1;
    p347_fpga_info.transfer.bits_per_word = 32;
    p347_fpga_info.transfer.speed_hz = p347_fpga_info.new_hz;
    p347_fpga_info.transfer.tx_buf = p347_fpga_info.spi_tx_buff;
    p347_fpga_info.transfer.rx_buf = p347_fpga_info.spi_rx_buff;

    spi_message_add_tail(&p347_fpga_info.transfer,&p347_fpga_info.msg);

    //sleep until complete
    //TODO: test, think about hanging up, do something
    ret = spi_sync(p347_fpga_info.spidev,&p347_fpga_info.msg);
    //_PDBA("spi_sync in spi_write_adc_register ret=%d\n",ret);
    if (ret != 0) return ret;

    //and maybe add check

    return 0;
}

int spi_write_fpga_register(unsigned char reg_idx, unsigned short reg_val)
{
    int ret;
    //request register writing
    p347_fpga_info.spi_tx_buff[0] = (1<<28) | (5<<24) | (reg_idx<<16) | (reg_val);
    p347_fpga_info.spi_tx_buff[1] = 0; //wait

    spi_message_init(&p347_fpga_info.msg);

    p347_fpga_info.transfer.len = 2*sizeof(u32); //1 word
    p347_fpga_info.transfer.cs_change = 1;
    p347_fpga_info.transfer.bits_per_word = 32;
    p347_fpga_info.transfer.speed_hz = p347_fpga_info.new_hz;
    p347_fpga_info.transfer.tx_buf = p347_fpga_info.spi_tx_buff;
    p347_fpga_info.transfer.rx_buf = p347_fpga_info.spi_rx_buff;

    spi_message_add_tail(&p347_fpga_info.transfer,&p347_fpga_info.msg);

    //sleep until complete
    //TODO: test, think about hanging up, do something
    ret = spi_sync(p347_fpga_info.spidev,&p347_fpga_info.msg);
    //_PDBA("spi_sync in spi_write_fpga_register ret=%d\n",ret);
    if (ret != 0) return ret;

//TODO: maybe someday uncomment check
/*
    //and read status register to check
    p347_fpga_info.spi_tx_buff[0] = (0x5<<24) | (p347_FPGA_REG_STATUS << 16);
    p347_fpga_info.spi_tx_buff[1] = 0; 

    spi_message_init(&p347_fpga_info.msg);

    p347_fpga_info.transfer.len = 2*sizeof(u32); //2 words
    p347_fpga_info.transfer.cs_change = 1;
    p347_fpga_info.transfer.bits_per_word = 32;
    p347_fpga_info.transfer.speed_hz = p347_fpga_info.new_hz;
    p347_fpga_info.transfer.tx_buf = p347_fpga_info.spi_tx_buff;
    p347_fpga_info.transfer.rx_buf = p347_fpga_info.spi_rx_buff;

    spi_message_add_tail(&p347_fpga_info.transfer,&p347_fpga_info.msg);

    //sleep until complete
    //TODO: test, think about hanging up, do something
    ret = spi_sync(p347_fpga_info.spidev,&p347_fpga_info.msg);
    _PDBA("spi_sync in spi_write_fpga_register check ret=%d\n",ret);
    if (ret != 0) return ret;
    //TODO: check error bits
 */
    //*reg_val = (p347_fpga_info.spi_rx_buff[1] & 0x0000FFFF);
    //_PDBA("rec[0]=0x%8x, rec[1]=0x%8x, reg_val=0x%4x\n",p347_fpga_info.spi_rx_buff[0],p347_fpga_info.spi_rx_buff[1],*reg_val);
    
    return 0;
}


int adc_set_params(unsigned char adc_idx, t_adc_params *par, u16 buf_len)
{
    int ret;
    if ((!par) || (adc_idx < 1) || (adc_idx > p347_ADC_CHANNELS_CNT)) return -EINVAL;
    if (atomic_read(&(tpi.ABUF[adc_idx-1].is_running))) return -EBUSY;
    
    //copy to inner structure
    memcpy(&(tpi.ABUF[adc_idx-1].par),par,sizeof(t_adc_params));
    //TODO: check need_len when final pack structure would applied
    tpi.ABUF[adc_idx-1].need_len = buf_len*2;
    TRY_SET(p347_ADC_REG_CONTROL_2,	par->control2);
    TRY_SET(p347_ADC_REG_OFFSET,	par->offset);
    TRY_SET(p347_ADC_REG_OVERRANGE,	par->overrange);
    TRY_SET(p347_ADC_REG_FILT_COEFF,	par->filt_coeff);
    TRY_SET(p347_ADC_REG_CH_SETTINGS,	par->ch_settings);
    TRY_SET(p347_ADC_REG_GAIN,		par->gain);
    TRY_SET(p347_ADC_REG_CONTROL_1,	par->control1);
    
    return 0;
}

int adc_run(unsigned char adc_idx)
{
    int ret;
    if (adc_idx < 1) return -EINVAL;
    if IS_CH_RUNNING(adc_idx-1) return -EBUSY;
    
    //logic flag should be before real switching,
    //bacause first data may come before flag sets
    atomic_set(&(tpi.ABUF[adc_idx-1].is_running),1);
    _PDBA("ADC %d run\n",adc_idx);

    //switch on channel in FPGA
    tpi.fparam.settings |= (0x1 << (adc_idx-1+4));
    tpi.fparam.settings |= 0x8000;
    TRY_SET_FPGA(p347_FPGA_REG_SETTINGS,tpi.fparam.settings);

    return 0;
}

int adc_run_all(void)
{   //TODO: add running check or remove
    int i,ret;

    for (i=0; i<p347_ADC_CHANNELS_CNT; i++)
	atomic_set(&(tpi.ABUF[i].is_running),1);

    tpi.fparam.settings |= 0x80F0;
    TRY_SET_FPGA(p347_FPGA_REG_SETTINGS,tpi.fparam.settings);

    return 0;
}

int adc_stop(unsigned char adc_idx)
{
    int ret;
    if (adc_idx < 1) return -EINVAL;
    if (!IS_CH_RUNNING(adc_idx-1)) return -EBUSY;
    
    _PDBA("ADC%d stop\n",adc_idx);
#ifdef CS67_MODE
    gpio_set_value(p347_GPIO_OSCIRQ,0);
#endif

    //TODO: maybe set some control params
    //switch off channel power supply
    tpi.fparam.settings &= ~(0x1 << (adc_idx-1+4)); //switch off ON_CHx
    if ((tpi.fparam.settings & 0x00F0) == 0) tpi.fparam.settings &= 0x7FFF; //switch off BRAM
    TRY_SET_FPGA(p347_FPGA_REG_SETTINGS,tpi.fparam.settings);

    atomic_set(&(tpi.ABUF[adc_idx-1].is_running),0);

#ifdef CS67_MODE
    gpio_set_value(p347_GPIO_OSCIRQ,1);
#endif

    return 0;
}

int adc_stop_all(void)
{
    int i,ret;
    //TODO: add running check or remove
    //TODO: maybe set some control params
    //switch off all channels
    tpi.fparam.settings &= 0x7F0F; //clear BRAM and all ON_CHx
    TRY_SET_FPGA(p347_FPGA_REG_SETTINGS,tpi.fparam.settings);

    for (i=0; i<p347_ADC_CHANNELS_CNT; i++)
	atomic_set(&(tpi.ABUF[i].is_running),0);

    return 0;
}

//==========================================================================
//==========================================================================
//==========================================================================

//static int p347_fpga_ioctl(struct inode *inode, struct file *filp, uint cmd, unsigned long arg)
static long p347_fpga_ioctl(struct file *filp, uint cmd, unsigned long arg)
{
    t_gpmc_data*	setup_g;
    t_spi_data*		setup_s;
    t_ch_params*	ch_p;
    t_transfer_params*	tp;
    t_data_buffer*	dbuf;
    t_selfgeneration*	sg;
    long ret_code = 0;
    char kdata[20];
    int i,wcnt,ret,len,p1,p2 = 0;
    unsigned short* us = (unsigned short*)&kdata[0];
    unsigned short reg_idx = 0;
    unsigned short reg_val = 0;
    unsigned short adc_idx;
    unsigned short tmps = 0;
    unsigned long flags = 0;
    //unsigned char testdata[10] = {1,2,3,4,5,6,7,8,9,0};
    siginfo_t usig;
    
    //lock_kernel();
    
    memset(&kdata[0],0,20);
    //_PDBA("p347_fpga_ioctl start: inode=0x%p, filp=0x%p, cmd=%d, arg=0x%p\n",(void*)inode,(void*)filp,cmd,(void*)arg);
    switch (cmd) {
	//----------------------------------------------------------------------------------------------GPMC
	case p347_IOCTL_WRITE: { //test write
	    if (arg != 0) {
#ifdef CS67_MODE
		_PDB("CS67_MODE active, so gpmc writing is not supported\n");
#else
		setup_g = (t_gpmc_data*)arg;
		ret = copy_from_user(&kdata[0],setup_g->data,setup_g->word_cnt*sizeof(u16));
		if (ret > 0) _PDBA("p347_IOCTL_WRITE: cannot copy %d bytes from userspace\n",ret);
		for (wcnt=0; wcnt<setup_g->word_cnt; wcnt++)
		    __raw_writew(&kdata[wcnt*2],fpga_mem_write_addr);
#endif
	    }
	break; }
	case p347_IOCTL_READ: {
	    if (arg != 0) {
		setup_g = (t_gpmc_data*)arg;
		*us = __raw_readw(fpga_mem_read_addr);
		ret = copy_to_user(setup_g->data,&kdata[0],2);
		if (ret > 0) _PDBA("p347_IOCTL_READ: cannot copy %d bytes to userspace\n",ret); 
		_PDBA("READ %02x\n",*us);
	    }
	break; }
	//----------------------------------------------------------------------------------------------SPI
	case p347_IOCTL_SPI_WRITE: { //test spi write, answer is not supposed
	    if (arg != 0) {
		setup_s = (t_spi_data*)arg;
		ret = copy_from_user(p347_fpga_info.spi_tx_buff,setup_s->data_tx,setup_s->word_cnt*sizeof(u32));
		if (ret > 0) _PDBA("p347_IOCTL_READ: cannot copy %d bytes from userspace\n",ret);
		p347_fpga_info.transfer.len = setup_s->word_cnt;
		
		spi_message_init(&p347_fpga_info.msg);
		spi_message_add_tail(&p347_fpga_info.transfer,&p347_fpga_info.msg);
		
		//sleep until complete
		//TODO: test, think about hanging up, do something
		ret = spi_sync(p347_fpga_info.spidev,&p347_fpga_info.msg);
		_PDBA("spi_sync in SPI_WRITE ret=%d\n",ret);
	    }
	break; }
	case p347_IOCTL_SPI_SENDCMD: { //send command and wait for answer
	    if (arg != 0) {
		setup_s = (t_spi_data*)arg;
		
		spi_message_init(&p347_fpga_info.msg);
		
		ret = copy_from_user(p347_fpga_info.spi_tx_buff,setup_s->data_tx,setup_s->word_cnt*sizeof(u32));
		if (ret > 0) _PDBA("p347_IOCTL_READ: cannot copy %d bytes from userspace\n",ret);
		
		p347_fpga_info.transfer.len = setup_s->word_cnt*sizeof(u32);
		p347_fpga_info.transfer.bits_per_word = 32;
		p347_fpga_info.transfer.speed_hz = p347_fpga_info.new_hz;
		p347_fpga_info.transfer.tx_buf = p347_fpga_info.spi_tx_buff;
		p347_fpga_info.transfer.rx_buf = p347_fpga_info.spi_rx_buff;
		
		_PDBA("rx_buff = 0x%p, tx_buff = 0x%p\n",p347_fpga_info.spi_rx_buff,p347_fpga_info.spi_tx_buff);
		
		spi_message_add_tail(&p347_fpga_info.transfer,&p347_fpga_info.msg);
		
		//sleep until complete
		//TODO: test, think about hanging up, do something
		ret = spi_sync(p347_fpga_info.spidev,&p347_fpga_info.msg);
		_PDBA("spi_sync in SPI_SENDCMD ret=%d\n",ret);
		//_PDBA("data_dst = 0x%p, data_src = 0x%p, words = %d\n",setup_s->data_rx,p347_fpga_info.spi_rx_buff,setup_s->word_cnt);
		ret = copy_to_user(setup_s->data_rx,p347_fpga_info.spi_rx_buff,setup_s->word_cnt*sizeof(u32));
		if (ret > 0) _PDBA("p347_IOCTL_READ: cannot copy %d bytes to userspace\n",ret);
		for (wcnt=0; wcnt<setup_s->word_cnt; wcnt++)
		    _PDBA("SPI answer[%d]=0x%x\n",wcnt,p347_fpga_info.spi_rx_buff[wcnt]);
	    }
	break; }
	case p347_IOCTL_SPI_READ_REGISTER: {
	    if (arg == 0) {
		ret_code -p347_ERROR_SPI_INVAL_ARG;
		break;
	    }
	    setup_s = (t_spi_data*)arg;
	    reg_idx = ((unsigned short)(setup_s->data_tx[0]));
	    adc_idx = ((unsigned short)(setup_s->data_tx[1]));
            if ((reg_idx < 1) || (reg_idx > p347_FPGA_REG_TEST_WRITE)) {
		ret_code = -p347_ERROR_SPI_INVAL_REG_NUM;
		break;
	    }

	    if (reg_idx < p347_FPGA_REG_SETTINGS) {//read adc register
		ret = spi_read_adc_register(adc_idx,reg_idx,&reg_val);
	    } else {//read fpga register
		ret = spi_read_fpga_register(reg_idx,&reg_val);
	    }
	    if (ret != 0) {
		ret_code = ret;
		break;
	    }
	
	    reg_val = (p347_fpga_info.spi_rx_buff[1] >> 16);
	    _PDBA("rec[0]=0x%08x, rec[1]=0x%08x, reg_val=0x%04x\n",p347_fpga_info.spi_rx_buff[0],p347_fpga_info.spi_rx_buff[1],reg_val);

	    ret = copy_to_user(setup_s->data_rx,&reg_val,2);
	    if (ret != 0) {
		ret_code = ret;
		break;
	    }
	break; }
	case p347_IOCTL_SPI_WRITE_REGISTER: {
	    if (arg == 0) return -p347_ERROR_SPI_INVAL_ARG;
	    setup_s = (t_spi_data*)arg;
	    reg_idx = ((unsigned short)(setup_s->data_tx[0]));
	    reg_val = ((unsigned short)(setup_s->data_tx[1]));
            adc_idx = ((unsigned short)(setup_s->data_tx[2]));
            if ((reg_idx < 1) || (reg_idx > p347_FPGA_REG_TEST_WRITE)) {
		ret_code = -p347_ERROR_SPI_INVAL_REG_NUM;
		break;
	    }

	    if (reg_idx < p347_FPGA_REG_SETTINGS) {//write adc register
	        ret = spi_write_adc_register(adc_idx,reg_idx,reg_val);
	    } else {//write fpga register
		ret = spi_write_fpga_register(reg_idx,reg_val);
	    }
	    if (ret != 0) {
	        ret_code = ret;
	        break;
	    }
	
	break; }
	case p347_IOCTL_SPI_SET_HZ: {
	    if ((arg >= p347_SPI_MIN_SPEED) && (arg <= p347_fpga_info.spidev->max_speed_hz)) {
		_PDBA("Modify spi transfer speed from %ld to %ld\n",p347_fpga_info.new_hz,arg);
		p347_fpga_info.new_hz = arg;
	    } else {
		_PDBA("Incorrect input HZ for SPI, should be between 1.5kHZ and %d\n",p347_fpga_info.spidev->max_speed_hz);
		_PDB("You must use speed definitions from p347_fpga_user.h\n");
	    }
	break; }
	//----------------------------------------------------------------------------------------------Client
	case p347_IOCTL_CLIENT_REGISTER: {
	    _PDBA("Calling client register, pid =%ld\n",arg);
	    
	    if (arg != 0) {
		tpi.client_pid = (arg & 0xFFFF);
		//ret = sigqueue(tpi.client_pid, SIGUSR2, tpi.client_pid); //confirm pid by signal
		//usig.si_signo = -SIGUSR2;
		usig.si_signo = SIGIO;
		usig.si_code = SI_USER;
		//usig.si_code = SI_ASYNCIO;
		//usig.si_code = SI_QUEUE;
		//usig.si_pid = current->pid;
		//usig.si_uid = current->tgid;
		usig.si_errno = 231;
		usig.si_value.sival_int = 999;
		
		//printk("driver: current->pid= %d, current->tgid= %d\n",current->pid, current->tgid);
		//printk("driver: signo = %d, code= %d, pid = %d, uid = %d, sival_int = %d\n",
		//    usig.si_signo,usig.si_code,usig.si_pid,usig.si_uid,usig.si_value.sival_int);
		//kill_pid(
		//kill_proc_info(SIGUSR2,&usig,tpi.client_pid);
		
		send_sig_info(SIGUSR2,&usig,pid_task(find_get_pid(tpi.client_pid),PIDTYPE_PID));
		//ret = sys_rt_sigqueueinfo(tpi.client_pid, SIGUSR2, &usig);
		//ret = do_rt_tgsigqueueinfo(tpi.client_pid,tpi.client_pid, SIGUSR2, &usig);
		//ret = do_send_sig_info
		/*
		tpi.skb = alloc_skb(10,GFP_KERNEL);
		if (tpi.skb != NULL) {
		    tpi.nlh = NLMSG_PUT(tpi.skb,0,1,NLMSG_DONE,10);
		    ret = netlink_unicast(tpi.nl_socket,tpi.skb,tpi.client_pid,0);
		    printk("netlink_unicast ret %d\n",ret);
		    return 0;
		}
		*/
	    } else {
		ret_code = -p347_ERROR_SYSTEM_INVAL_PID;
		break;
	    }
//nlmsg_failure:
//		kfree_skb(tpi.skb);
	break; }
	case p347_IOCTL_CLIENT_UNREGISTER: {
		//tpi.client_pid = -1;
	    
		usig.si_signo = -SIGUSR2;
		//usig.si_code = SI_ASYNCIO;
		usig.si_code = SI_QUEUE;
		//usig.si_pid = current->pid;
		//usig.si_uid = current->tgid;
		usig.si_value.sival_int = 543;
		usig.si_errno = 987;
		
		send_sig_info(SIGUSR2,&usig,pid_task(find_get_pid(tpi.client_pid),PIDTYPE_PID));
		tpi.client_pid = -1;
	break; }
	case p347_IOCTL_CLIENT_SETUP_CHANNEL: {
	    if (arg != 0) {
		ch_p = (t_ch_params*)arg;
                if (ch_p->len > p347_PUMPBUF_MAXRECORDS) {
            	    ret_code = -ENOMEM; //len comes in 2*16bit data records
            	    break;
            	}
                _PDBA("setup channel %d, len = %d data records\n",ch_p->idx,ch_p->len);
		adc_set_params(ch_p->idx,&(ch_p->apar),ch_p->len);
	    } else {
		ret_code = -p347_ERROR_SYSTEM_INVAL_SETUP;
		break; 
	    }
	break; }
	case p347_IOCTL_CLIENT_PERFORM_TRANSFER: {
	    if (arg != 0) {
		tp = (t_transfer_params*)arg;
		if (tp->u_ptr == NULL) {
		    ret_code = -EINVAL;
		    break;
		}
		//ch_idx comes from user in 1..N mode, not 0..N-1
		if ((tp->ch_idx < 1) || (tp->ch_idx > p347_ADC_CHANNELS_CNT)) {
		    return -EINVAL;
		    break;
		}
		//_PDB("PERFORM_TRANSFER CALL\n");
		dbuf = &(tpi.ABUF[tp->ch_idx-1]);
		//check is there really data for transfer
		if (atomic_read(&(dbuf->cur_len)) >= dbuf->need_len) {
		    //_PDB("Copying data to user!\n");
		    //copy, but check first for length overlap
		    wcnt = atomic_read(&(dbuf->read_pos));
		    len = wcnt + dbuf->need_len;
		    spin_lock_irqsave(&(dbuf->lock),flags);
		    if (len <= p347_PUMPBUF_FULLSIZE) { //copy full buffer at once
			copy_to_user(tp->u_ptr,dbuf->ptr+wcnt*sizeof(u16),dbuf->need_len*sizeof(u16));
			wcnt += dbuf->need_len;
			if (wcnt >= p347_PUMPBUF_FULLSIZE) wcnt = 0;
			atomic_set(&(dbuf->read_pos),wcnt);
		    } else { //copy part from rpos to end and from start to need
			p1 = p347_PUMPBUF_FULLSIZE-wcnt;
			copy_to_user(tp->u_ptr,dbuf->ptr+wcnt*sizeof(u16),p1*sizeof(u16));//first part
			p2 = dbuf->need_len - p1; //what is left after first part
			copy_to_user(tp->u_ptr+p1*sizeof(u16),dbuf->ptr,p2*sizeof(u16)); //second part
			atomic_set(&(dbuf->read_pos),p2);
		    }
		    tmps = atomic_read(&(dbuf->cur_len));
		    tmps -= dbuf->need_len;
		    atomic_set(&(dbuf->cur_len),tmps);
		    spin_unlock_irqrestore(&(dbuf->lock),flags);
		} else {
		    ret_code = -ENODATA;
		    break; 
		}
	    } else {
		ret_code = -EINVAL;
	    }
	break; }
	case p347_IOCTL_CLIENT_CHECK_DATA: {
	    if (arg != 0) {
		for (i=0; i<p347_ADC_CHANNELS_CNT; i++)
		    if ( (atomic_read(&(tpi.ABUF[i].cur_len)) >= tpi.ABUF[i].need_len)
			 && (IS_CH_RUNNING(i) > 0) ) {
			//_PDBA("PACK IS READY FOR CHANNEL %d\n",i+1);
			put_user(1,(unsigned char*)(arg+i));
		    }
	    } else {
		ret_code = -EINVAL;
		break;
	    }
	break; }
	case p347_IOCTL_CLIENT_START_CHANNEL: {
	    if ((arg > 0) && (arg <= p347_ADC_CHANNELS_CNT)) {
		dbuf = &(tpi.ABUF[arg-1]);
		spin_lock_irqsave(&(dbuf->lock),flags);
		    //clear buffer pointers
		    atomic_set(&(dbuf->cur_len),0);
		    atomic_set(&(dbuf->read_pos),0);
		    atomic_set(&(dbuf->write_pos),0);
		    //fill it with magic numbers
		    for (i=0; i<p347_PUMPBUF_FULLSIZE; i+=2) {
			dbuf->ptr[i] = 0xDEAD;
			dbuf->ptr[i+1] = 0xBEEF;
		    }
		spin_unlock_irqrestore(&(dbuf->lock),flags);
	        //and run
	        ret_code = adc_run((unsigned char)arg);
	    } else
		ret_code = -EINVAL;
	break; }
	case p347_IOCTL_CLIENT_STOP_CHANNEL: {
	    if ((arg > 0) && (arg <= p347_ADC_CHANNELS_CNT)) {
	        ret_code = adc_stop((unsigned char)arg);
	    } else
		ret_code = -EINVAL;
	break; }
	case p347_IOCTL_GET_INT_CNT: {
	    if (arg>0) {
		put_user(p347_fpga_info.i_cnt,(unsigned long*)arg);
	    }
	break; }
	case p347_IOCTL_GENERATE_PULSE: {
	    if (arg>0) {
		//usleep_range for useconds
		sg = (t_selfgeneration*)arg;
		if ((sg->count <1) || (sg->us_width<1) || (sg->us_idle<1)) {
		
		}
	    }
	break; }
	default: {
	    _PDB("unknown cmd\n");
	break; }
    };
    //_PDB("p347_fpga_ioctl end\n");
    
    //unlock_kernel();
    return ret_code;
}
/*
static void nl_callback(struct sk_buff *__skb)
{
    printk("p347 nl_callback\n");
}
*/
//==========================================================================

static const struct file_operations p347_fpga_fops = {
    .owner		= THIS_MODULE,
    .unlocked_ioctl	= p347_fpga_ioctl,
    .open		= p347_fpga_open,
    .release		= p347_fpga_release,
    .fasync		= NULL,
    .llseek		= no_llseek,
};

//==========================================================================
//==========================================================================
//==========================================================================

static void p347_spi_complete(void)
{
    _PDB("p347_spi_complete call\n");
}

static int __devinit p347_spi_probe(struct spi_device *spi)
{
    int ret;
    _PDBA("__devinit p347_spi_probe, spi=0x%p\n",(void*)spi);
    p347_fpga_info.spidev = spi;
    
    spi->bits_per_word = 32;
    spi->mode = SPI_MODE_0;
    ret = spi_setup(spi);
    _PDBA("spi_setup ret %d\n",ret);
    
    //update MCSPI_SYST register
    __raw_writel(0x00000100,ioremap(0x480B8000,SZ_4K)+0x24);
    
    _PDBA("master->mode=%x, master->flags=%x, spi->dev initname = %s\n",
	spi->master->mode_bits,spi->master->flags,spi->dev.init_name);
    
    spi_message_init(&p347_fpga_info.msg);
    p347_fpga_info.msg.complete = p347_spi_complete;
    
    return ret;
};

//==========================================================================

static int __devexit p347_spi_remove(struct spi_device *spi)
{
    _PDB("__devexit p347_spi_remove\n");
    
    return 0;
};

static int p347_spi_suspend(struct spi_device *spi, pm_message_t message)
{
    _PDBA("p347_spi_suspend, message=%d\n",message.event);
    return 0;
}

static int p347_spi_resume(struct spi_device *spi)
{
    _PDB("p347_spi_resume\n");
    return 0;
}

//==========================================================================

static struct spi_driver p347_spi_driver = {
    .driver = {
	.name 	= "p347_spi",
	.bus 	= &spi_bus_type,
	.owner 	= THIS_MODULE,
    },
    .probe 	= p347_spi_probe,
    .remove	= __devexit_p(p347_spi_remove),
    .suspend	= p347_spi_suspend,
    .resume	= p347_spi_resume,
};

//==========================================================================
//==========================================================================
//==========================================================================


#define set_read_print(cs,reg,var,name) \
    my_gpmc_cs_write_reg(cs, reg, var);\
    var = my_gpmc_cs_read_reg(cs, reg);\
    _PDBA("CS%d "name" reg value = 0x%x\n",cs,var);

void set_timings(unsigned char gcs) {
    unsigned char div,mult;
    u32 reg_val;

    div = 1;
    mult = 1;                     //22
    //cswrofftime = 5 ticks, csrdofftime = 15 ticks, csextradelay = 0, csontime = 0
    reg_val = (5*mult << 16) | (23*mult << 8) | (0 << 7) | (0*mult);
    set_read_print(gcs,GPMC_CS_CONFIG2, reg_val,"CONFIG2");
    //advwrofftime = 2, advrdofftime = 4, advextradelay = 0, advontime = 1
    reg_val = (2*mult << 16) | (4*mult << 8) | (0*mult << 7) | (1*mult);
    set_read_print(gcs,GPMC_CS_CONFIG3, reg_val,"CONFIG3");
    //weofftime = 5, weextradelay = 0, weontime = 3, oeofftime = 13, oeextradelay = 0, oeontime = 4
    reg_val = (5*mult << 24) | (0*mult << 23) | (3*mult << 16) | (22*mult << 8) | (0 << 7) | (6*mult);
    set_read_print(gcs,GPMC_CS_CONFIG4, reg_val,"CONFIG4");
    //pageburstaccesstime = 1, rdaccesstime = 6, wrcycletime = 7, rdcycletime = 15
    reg_val = (2*mult << 24) | (7*mult << 16) | (7*mult << 8) | (8*mult);//10
    set_read_print(gcs,GPMC_CS_CONFIG5, reg_val,"CONFIG5");
    //wraccesstime = 3, wrdataonadmuxbus = 2, cycle2cycledelay = 1
    //cycle2cyclesamecsen = 0, cycle2cyclediffcsen = 1, busturnaround = 1
    reg_val = my_gpmc_cs_read_reg(gcs, GPMC_CS_CONFIG6);
    reg_val &= 0x80000000; //do not modify bit 31
    
    //reg_val |= (3*mult << 24) | (2*mult << 16) | (0*mult << 8) | (0*mult << 7) | (0*mult << 6) | (0*mult);
    reg_val |= (3*mult << 24) | (2*mult << 16) | (1*mult << 8) | (0*mult << 7) | (1*mult << 6) | (1*mult);
    set_read_print(gcs,GPMC_CS_CONFIG6, reg_val,"CONFIG6");
    //config1
    reg_val = 0x68801000;
    if (GPMC_MHZ == 80)
	reg_val |= 0x11; //TIMEPARAGRANULARITY and GPMC_CLK = GPMC_FCLK/2
    set_read_print(gcs,GPMC_CS_CONFIG1, reg_val,"CONFIG1");
}

void print_gpmc_padconf(unsigned long base_addr) {
    unsigned long padconf_addr;
    unsigned long reg_val;
    int i;
    unsigned char bit_inen, bit_pullud, bit_pullen = 0;
    
    for (i=0; i<22; i++) {
	padconf_addr = base_addr + 0x207C + i*4;
	reg_val = __raw_readl(padconf_addr);
	printk("REG 0x%p: value 0x%p\n",(void*)padconf_addr,(void*)reg_val);
	if (reg_val & 0x01000000) bit_inen = 1; else bit_inen = 0;
	if (reg_val & 0x00100000) bit_pullud = 1; else bit_pullud = 0;
	if (reg_val & 0x00080000) bit_pullen = 1; else bit_pullen = 0;
	printk("    High word: INPUTENABLE=%d, PULLUP/DOWN=%d, PULLENABLE=%d\n",bit_inen,bit_pullud,bit_pullen);
	if (reg_val & 0x00000100) bit_inen = 1; else bit_inen = 0;
	if (reg_val & 0x00000010) bit_pullud = 1; else bit_pullud = 0;
	if (reg_val & 0x00000008) bit_pullen = 1; else bit_pullen = 0;
	printk("    Low word: INPUTENABLE=%d, PULLUP/DOWN=%d, PULLENABLE=%d\n",bit_inen,bit_pullud,bit_pullen);
    }
}

void print_clken_registers(void) {
    unsigned short mul,div,m2,dl3,dl4,src = 0;
    unsigned long cm_base = (unsigned long)ioremap(0x48000000,SZ_4K);
    unsigned long reg_val = __raw_readl(cm_base+0x4A00);
    printk("CM_FCLKEN1_CORE = 0x%08lx\n",reg_val);
    reg_val = __raw_readl(cm_base+0x4A10);
    printk("CM_ICLKEN1_CORE = 0x%08lx\n",reg_val);
    //--------------------------------------------------
    reg_val = __raw_readl(cm_base+0x4924);
    if (reg_val & 0x1) printk("MPU_CLK is locked to SYS_CLK\n");
    else printk("MPU_CLK is bypassed and = DPLL1_FCLK\n");
    //--
    reg_val = __raw_readl(cm_base+0x4940);
    mul = (reg_val >> 8) & 0x7FF;
    div = reg_val & 0x7F;
    src = (reg_val >> 19) & 0x7;
    printk("CM_CLKSEL1_PLL_MPU = 0x%08lx, mult=%d, div=%d, m2=%d\n",reg_val,mul,div,src);
    reg_val = __raw_readl(cm_base+0x4944);
    div = reg_val & 0x1F;
    printk("CM_CLKSEL2_PLL_MPU = 0x%08lx, div=%d\n",reg_val,div);
    //--------------------------------------------------
    reg_val = __raw_readl(cm_base+0x4d40);
    mul = (reg_val >> 16) & 0x7FF;
    div = (reg_val >> 8) & 0x7F;
    m2 = (reg_val >> 27) & 0x1F;
    printk("CM_CLKSEL1_PLL = 0x%08lx, mult=%d, div=%d, M2=%d\n",reg_val,mul,div,m2);
    reg_val = __raw_readl(cm_base+0x4a40);
    dl3 = (reg_val & 0x3);
    dl4 = (reg_val >> 2) & 0x3;
    printk("CM_CLKSEL_CORE = 0x%08lx, dl3=%d, dl4=%d\n",reg_val,dl3,dl4);
    cm_base = ioremap(0x48300000,SZ_4K);
    reg_val = __raw_readl(cm_base+0x6d40);
    printk("PRM_CLKSEL = 0x%08lx\n",reg_val);
    printk("Bits[2:0] -> 0=12Mhz, 1=13, 2=19.2, 3= 26, 4=38.4, 5=16.8\n");
}

#define psp_padconf(addr,offset,value)\
    padconf_addr = scm_base + addr;\
    reg_val = __raw_readl(padconf_addr);\
    if (offset) reg_val &= 0x0000FFFF; else reg_val &= 0xFFFF0000;\
    reg_val |= (value << offset);\
    __raw_writel(reg_val,padconf_addr)

static int __init p347_fpga_init(void)
{
    int ret,i;
    u32 reg_val,old;
    size_t tmp_sz = 0;
    unsigned long rate;
    unsigned long scm_base = (unsigned long)ioremap(0x48000000,SZ_4K);
    unsigned long cm_addr = scm_base + 0x4d40;
    unsigned long padconf_addr;
#ifdef YURI_BOARD
    //unsigned long gpio_base = (unsigned long)ioremap(0x48310000,SZ_4K); //GPIO bank 1
    unsigned long gpio_base = (unsigned long)ioremap(0x49054000,SZ_4K); //GPIO bank 4
#else
    unsigned long gpio_base = (unsigned long)ioremap(0x49054000,SZ_4K); //GPIO bank 4
#endif
    unsigned long dmareg_base = (unsigned long)ioremap(0x48056000,SZ_4K);
    unsigned long mpuintc_base = (unsigned long)ioremap(0x48200000,SZ_4K);
    unsigned long tmp_addr;
    
    spin_lock_init(&irq_lock);
    
    tpi.client_pid = -1;
    //TODO: universal free-and-clear function
    
    _PDB("p347_fpga_init start\n");
    print_clken_registers();
    
    p347_fpga_info.err_cnt = 0;
    //ret = irq_to_gpio(161);
    //_PDBA("irq 161 is gpio%d\n",ret);
    
    //ret = gpmc_cs_request(p347_GPMC_FPGA_CS_READ,SZ_4K,&fpga_mem_read_base);
    ret = gpmc_cs_request(p347_GPMC_FPGA_CS_READ,SZ_16M,&fpga_mem_read_base);
    _PDBA("gpmc_cs_request READ ret=%d\n",ret);
    fpga_mem_read_addr = (unsigned long)ioremap(fpga_mem_read_base,SZ_4K);
    //ret = gpmc_cs_request(p347_GPMC_FPGA_CS_WRITE,SZ_4K,&fpga_mem_write_base);
    ret = gpmc_cs_request(p347_GPMC_FPGA_CS_WRITE,SZ_16M,&fpga_mem_write_base);
    _PDBA("gpmc_cs_request WRITE ret=%d\n",ret);
    fpga_mem_write_addr = (unsigned long)ioremap(fpga_mem_write_base,SZ_4K);

    _PDBA("read mem base=0x%p, remap=0x%p\n",(void*)fpga_mem_read_base,(void*)fpga_mem_read_addr);
    //_PDBA("write mem base=0x%p, remap=0x%p\n",(void*)fpga_mem_write_base,(void*)fpga_mem_write_addr);

    memset(&p347_fpga_info,0,sizeof(p347_fpga_device_t));
    p347_fpga_info.i_cnt = 0;

    //-----------------------------------------------------------memory allocations
    tmp_sz = DATA_FRAME_SIZE*RD_BURST_LEN*2; //dma buffer size
    d_ret = dma_alloc_coherent(NULL,tmp_sz,&p347_fpga_info.dma_rd_addr,GFP_DMA);
    if (d_ret == NULL) {
	_PDBA("Cannot allocate space for gpmc dma rx buffer (%d bytes)\n",tmp_sz);
	return -ENOMEM;
    }
    _PDBA("dma_alloc_coherent ret=0x%p, addr =0x%08x\n",d_ret,p347_fpga_info.dma_rd_addr);
    
    tmp_sz = SPI_RXBUFF_LEN*sizeof(u32);
    p347_fpga_info.spi_rx_buff = kzalloc(tmp_sz,GFP_KERNEL);
    if (p347_fpga_info.spi_rx_buff == NULL) {
	_PDBA("Cannot allocate space for spi_rx_buff (%d bytes)\n",tmp_sz);
	dma_free_coherent(NULL,RD_BURST_LEN*2,d_ret,p347_fpga_info.dma_rd_addr);
	return -ENOMEM;
    }
    _PDBA("spi_rx_buff kzallocated in 0x%p\n",p347_fpga_info.spi_rx_buff);
    
    tmp_sz = SPI_TXBUFF_LEN*sizeof(u32);
    p347_fpga_info.spi_tx_buff = kzalloc(tmp_sz,GFP_KERNEL);
    if (p347_fpga_info.spi_tx_buff == NULL) {
	_PDBA("Cannot allocate space for spi_tx_buff (%d bytes)\n",tmp_sz);
	dma_free_coherent(NULL,RD_BURST_LEN*2,d_ret,p347_fpga_info.dma_rd_addr);
	kfree(p347_fpga_info.spi_rx_buff);
	return -ENOMEM;
    }
    _PDBA("spi_tx_buff kzallocated in 0x%p\n",p347_fpga_info.spi_tx_buff);
    
    //-----------------------------------------------------------gpmc_clk pad configuration
    padconf_addr = scm_base + PADCONF_GPMC_CLK_ADDR;
    reg_val = __raw_readl(padconf_addr);
    _PDBA("read clk padconf 0x%p ret 0x%x\n",(void*)padconf_addr,reg_val);
    reg_val &= 0x0000FFFF;
    reg_val |= 0x01100000;
    __raw_writel(reg_val,padconf_addr);
    reg_val = __raw_readl(padconf_addr);
    _PDBA("read clk padconf 0x%p ret 0x%x\n",(void*)padconf_addr,reg_val);
    
    //-----------------------------------------------------------fpga_irq pad configuration
    padconf_addr = scm_base + PADCONF_FPGA_IRQ_ADDR;
    reg_val = __raw_readl(padconf_addr);
    _PDBA("read irq padconf 0x%p ret 0x%x\n",(void*)padconf_addr,reg_val);
    if (PADCONF_FPGA_IRQ_OFFSET) reg_val &= 0x0000FFFF; else reg_val &= 0xFFFF0000;
    reg_val |= (PADCONF_FPGA_IRQ_VALUE << PADCONF_FPGA_IRQ_OFFSET);
    __raw_writel(reg_val,padconf_addr);
    reg_val = __raw_readl(padconf_addr);
    _PDBA("read irq padconf 0x%p ret 0x%x\n",(void*)padconf_addr,reg_val);

    //-----------------------------------------------------------mcspi3 pad configuration
    psp_padconf(PADCONF_SPI3_SIMO_ADDR,PADCONF_SPI3_SIMO_OFFSET,PADCONF_SPI3_SIMO_VALUE);
    psp_padconf(PADCONF_SPI3_SOMI_ADDR,PADCONF_SPI3_SOMI_OFFSET,PADCONF_SPI3_SOMI_VALUE);
    psp_padconf(PADCONF_SPI3_CS1_ADDR,PADCONF_SPI3_CS1_OFFSET,PADCONF_SPI3_CS1_VALUE);
    psp_padconf(PADCONF_SPI3_CLK_ADDR,PADCONF_SPI3_CLK_OFFSET,PADCONF_SPI3_CLK_VALUE);
    psp_padconf(PADCONF_SPI3_CS0_ADDR,PADCONF_SPI3_CS0_OFFSET,PADCONF_SPI3_CS0_VALUE);
    
    //-----------------------------------------------------------
    
    gpmc_base = ioremap(0x6e000000,SZ_4K);
    _PDBA("ioremap ret=0x%p\n",(void*)gpmc_base);
    
    //-----------------------------------------------------------dpll3 clock divider (make low rate)
  //comment if no need to modify system clock
    if (GPMC_MHZ < 80) {
	reg_val = __raw_readl(cm_addr);
	_PDBA("read addr 0x%p ret 0x%x\n",(void*)cm_addr,reg_val);
	reg_val &= 0x07FFFFFF;
	//reg_val |= (16 << 27); //2.66MHz = 166/4/16 (4 is gpmc_fclk divider)
	//reg_val |= (2<<27);
	if (GPMC_MHZ == 20) reg_val |= (8<<27); //166/8=20.75MHz
	if (GPMC_MHZ == 40) reg_val |= (4<<27); //166/8=41.5MHz
	__raw_writel(reg_val,cm_addr);
	reg_val = __raw_readl(cm_addr);
	_PDBA("read addr 0x%p ret 0x%x\n",(void*)cm_addr,reg_val);
    }
    gpmc_l3_clk = clk_get(NULL,"gpmc_fck");
    if (IS_ERR(gpmc_l3_clk)) {
	_PDB("Could not get GPMC clock for p347_fpga\n");
    }
    
    ret = clk_enable(gpmc_l3_clk);
    _PDBA("gpmc_l3_clk enable ret %d\n",ret);
    
    rate = clk_get_rate(gpmc_l3_clk);
    if (rate == 0) {
	printk(KERN_WARNING "gpmc_l3_clk not enabled\n");
	return -1;
    } else {
	_PDBA("gpmc_fck rate=%d\n",rate);
    }

    dpll3_clk = clk_get(NULL,"dpll3_m2_ck");
    if (IS_ERR(dpll3_clk)) {
	_PDB("Could not get dpll3 clock for p347_fpga\n");
    }

    rate = clk_get_rate(dpll3_clk);
    if (rate == 0) {
	printk(KERN_WARNING "dpll3_m2_ck not enabled\n");
	//return -1;
    } else {
	_PDBA("dpll3_m2_ck rate=%d\n",rate);
    }

#ifdef YURI_BOARD
    //clock enable (iclk) for GPIO1
    reg_val = __raw_readl(scm_base+0x4c10);
    _PDBA("default CM_ICLKEN_WKUP register = 0x%x\n",reg_val);
    reg_val = reg_val | 0x8; //set bit3 (EN_GPIO1) to 1
    __raw_writel(reg_val,scm_base+0x4c10);
    reg_val = __raw_readl(scm_base+0x4c10);
    _PDBA("new CM_ICLKEN_WKUP register = 0x%x\n",reg_val);
    //clock enable (fclk) for GPIO1
    reg_val = __raw_readl(scm_base+0x4c00);
    _PDBA("default CM_FCLKEN_WKUP register = 0x%x\n",reg_val);
    reg_val = reg_val | 0x8; //set bit3 (EN_GPIO1) to 1
    __raw_writel(reg_val,scm_base+0x4c00);
    reg_val = __raw_readl(scm_base+0x4c00);
    _PDBA("new CM_FCLKEN_WKUP register = 0x%x\n",reg_val);
    //print CM_IDLEST_WKUP
    reg_val = __raw_readl(scm_base+0x4c20);
    _PDBA("read-only CM_IDLEST_WKUP register = 0x%x\n",reg_val);
    //autoidle register
    reg_val = __raw_readl(scm_base+0x4c30);
    _PDBA("read-only CM_AUTOIDLE_WKUP register = 0x%x\n",reg_val);
#endif


    //-----------------------------------------------------------request pins
    ret = gpio_request(p347_GPIO_FPGA_IRQ,"gpio_fpga_irq");
    if (ret != 0) {
	_PDBA("ERROR: cannot request gpio_fpga_irq, ret=%d\n",ret);
	return ret;
    }
    _PDB("gpio_fpga_irq registered\n");
    
    p347_fpga_info.irqnum = gpio_to_irq(p347_GPIO_FPGA_IRQ);
    _PDBA("gpio_to_irq ret irqnum=%d\n",p347_fpga_info.irqnum);
    
    ret = request_irq(p347_fpga_info.irqnum,p347_fpga_irq_handler,0,"p347_fpga_irq",&p347_fpga_info);
    _PDBA("request_irq %d ret %d\n",p347_fpga_info.irqnum,ret);

    old = __raw_readl(gpio_base + 0x30); //GPIO_CTRL
    reg_val = 0; //enable module, no gating
    __raw_writel(reg_val,gpio_base + 0x30);
    reg_val = __raw_readl(gpio_base + 0x30);
    _PDBA("CTRL for GPIO old=0x%08x, new=0x%08x\n",old,reg_val);

    old = __raw_readl(gpio_base + 0x10); //GPIO_SYSCONFIG
    reg_val = 0x8; //No-idle mode
    __raw_writel(reg_val,gpio_base + 0x10);
    reg_val = __raw_readl(gpio_base + 0x10);
    _PDBA("SYSCONFIG for GPIO old=0x%08x, new=0x%08x\n",old,reg_val);
    
    __raw_writel(0,gpio_base + 0x20); //no wakeup enable
    
    old = __raw_readl(gpio_base + 0x34); //GPIO_OE
    reg_val = old | (1 << p347_GPIO_BITPOS); //set as input
    __raw_writel(reg_val,gpio_base + 0x34);
    reg_val = __raw_readl(gpio_base + 0x34);
    _PDBA("OE for GPIO old=0x%08x, new=0x%08x\n",old,reg_val);

    old = __raw_readl(gpio_base + 0x50); //GPIO_DEBOUNCEENABLE
    //reg_val = old & (~(1 << p347_GPIO_BITPOS)); //No debounce
    reg_val = old & (1 << p347_GPIO_BITPOS); //yes
    __raw_writel(reg_val,gpio_base + 0x50);
    reg_val = __raw_readl(gpio_base + 0x50);
    _PDBA("DEBOUNCEENABLE for GPIO old=0x%08x, new=0x%08x\n",old,reg_val);
/*
    reg_val = __raw_readl(gpio_base + 0x4048); //GPIO4 RISINGDETECT
    reg_val |= (1 << p347_GPIO_BITPOS);
    __raw_writel(reg_val,gpio_base + 0x4048);
*/
    reg_val = __raw_readl(gpio_base + 0x4C); //GPIO4 FALLINGDETECT
    reg_val |= (1 << p347_GPIO_BITPOS);
    __raw_writel(reg_val,gpio_base + 0x4C);

/*
    reg_val = __raw_readl(gpio_base + 0x4014); //GPIO_SYSSTATUS
    _PDBA("SYSSTATUS for GPIO4 = 0x%08x\n",reg_val);
    reg_val = __raw_readl(gpio_base + 0x4038); //GPIO_DATAIN
    _PDBA("DATAIN for GPIO4 = 0x%08x\n",reg_val);
*/    
    old = __raw_readl(gpio_base + 0x1C); //GPIO_IRQENABLE1
    reg_val = old | (1 << p347_GPIO_BITPOS);
    __raw_writel(reg_val,gpio_base + 0x1C);
    reg_val = __raw_readl(gpio_base + 0x1C);
    _PDBA("IRQENABLE1 for GPIO old=0x%08x, new=0x%08x\n",old,reg_val);

#ifdef CS67_MODE
    //request cs6 and cs7 as gpio
    ret = gpio_request(p347_GPIO_OSCIRQ,"gpio_oscirq");
    if (ret != 0) {
	_PDBA("ERROR: cannot request gpio_oscirq, ret=%d\n",ret);
	return ret;
    }
    ret = gpio_request(p347_GPIO_OSCDMA,"gpio_oscdma");
    if (ret != 0) {
	_PDBA("ERROR: cannot request gpio_oscdma, ret=%d\n",ret);
	return ret;
    }

    //pad configuration for CS6
    padconf_addr = scm_base + PADCONF_OSCIRQ;
    reg_val = __raw_readl(padconf_addr);
    reg_val &= 0x0000FFFF;
    reg_val |= (PADCONF_OSCIRQ_VALUE << PADCONF_OSCIRQ_OFFSET);
    __raw_writel(reg_val,padconf_addr);
    //pad configuration for CS7
    padconf_addr = scm_base + PADCONF_OSCDMA;
    reg_val = __raw_readl(padconf_addr);
    reg_val &= 0xFFFF0000;
    reg_val |= PADCONF_OSCDMA_VALUE;
    __raw_writel(reg_val,padconf_addr);
    
    gpio_direction_output(p347_GPIO_OSCIRQ,1);
    gpio_direction_output(p347_GPIO_OSCDMA,1);
#endif
#ifdef YURI_BOARD
    //request qpio for fpga reset
    ret = gpio_request(p347_GPIO_FPGA_RESET,"gpio_fpga_reset");
    if (ret != 0) {
	_PDBA("ERROR: cannot request gpio_fpga_reset, ret=%d\n",ret);
	return ret;
    }
    
    padconf_addr = scm_base + PADCONF_FPGA_RESET_ADDR;
    reg_val = __raw_readl(padconf_addr);
    reg_val &= 0x0000FFFF;
    reg_val |= (PADCONF_FPGA_RESET_VALUE << PADCONF_FPGA_RESET_OFFSET);
    __raw_writel(reg_val,padconf_addr);
    
    gpio_direction_output(p347_GPIO_FPGA_RESET,1);
#endif
    //-----------------------------------------------------------dma setup
    
    p347_fpga_info.dma_par.data_type = OMAP_DMA_DATA_TYPE_S16;
    p347_fpga_info.dma_par.src_start = fpga_mem_read_base;
    p347_fpga_info.dma_par.src_amode = OMAP_DMA_AMODE_POST_INC;
    p347_fpga_info.dma_par.dst_start = p347_fpga_info.dma_rd_addr;
    _PDBA("dst addr = 0x%08lx\n",p347_fpga_info.dma_par.dst_start);
    p347_fpga_info.dma_par.dst_amode = OMAP_DMA_AMODE_POST_INC;
    p347_fpga_info.dma_par.dst_ei = 1;
    p347_fpga_info.dma_par.dst_fi = 1;
    
    //p347_fpga_info.dma_par.elem_count = 8*64;
    //p347_fpga_info.dma_par.frame_count = 1;
    //p347_fpga_info.dma_par.sync_mode = OMAP_DMA_SYNC_BLOCK;
    //p347_fpga_info.dma_par.sync_mode = OMAP_DMA_SYNC_FRAME;
    
    p347_fpga_info.dma_par.elem_count = 8;
    //p347_fpga_info.dma_par.src_or_dst_sync = OMAP_DMA_SRC_SYNC;

    if (DATA_FRAME_SIZE > 1) {
	p347_fpga_info.dma_par.frame_count = DATA_FRAME_SIZE;
	p347_fpga_info.dma_par.sync_mode = OMAP_DMA_SYNC_BLOCK;
    } else {
	//One 8-words pack
	p347_fpga_info.dma_par.frame_count = 1;
	p347_fpga_info.dma_par.sync_mode = OMAP_DMA_SYNC_FRAME;
    }

    //-----------------------------------------------------------gpmc request and setup
        
    //set timings
    set_timings(p347_GPMC_FPGA_CS_READ);
    set_timings(p347_GPMC_FPGA_CS_WRITE);
    
#ifndef READ_NODMA
    p347_fpga_info.dma_ch = -1;
    ret = omap_request_dma(77,"p347_fpga_read_dma",p347_fpga_dma_callback,&p347_fpga_info,&p347_fpga_info.dma_ch);
    _PDBA("omap_request_dma ret %d, channel = %d\n",ret,p347_fpga_info.dma_ch);
    
    omap_set_dma_params(p347_fpga_info.dma_ch,&p347_fpga_info.dma_par);
    omap_set_dma_src_burst_mode(p347_fpga_info.dma_ch,OMAP_DMA_DATA_BURST_8);
    omap_set_dma_dest_burst_mode(p347_fpga_info.dma_ch,OMAP_DMA_DATA_BURST_8);
    
    reg_val = __raw_readl(dmareg_base+p347_fpga_info.dma_ch*0x60+0x90); //CSDP2
    reg_val |= 0x40; //bit6 - source provided packed data
    __raw_writel(reg_val,dmareg_base+p347_fpga_info.dma_ch*0x60+0x90);
    
    reg_val = __raw_readl(dmareg_base+p347_fpga_info.dma_ch*0x60+0x80); //CCR2
    reg_val |= 0x20; //bit5 - FS (copy by frame)
    reg_val |= 0x04000000; //bit26 - write priority to high
    reg_val |= 0x01000000; //bit24 - sync by src
    __raw_writel(reg_val,dmareg_base+p347_fpga_info.dma_ch*0x60+0x80);
#endif

    //tmp_addr = dmareg_base + p347_fpga_info.dma_ch*0x60;
//   _PDBA("GCR = 0x%08x\n",__raw_readl(dmareg_base+0x78));
    //_PDBA("CCR2 = 0x%08x\n",__raw_readl(tmp_addr+0x80));
/*    _PDBA("CLINK2 = 0x%08x\n",__raw_readl(tmp_addr+0x84));
    _PDBA("CICR2 = 0x%08x\n",__raw_readl(tmp_addr+0x88));
    _PDBA("CSR2 = 0x%08x\n",__raw_readl(tmp_addr+0x8C));
    _PDBA("CSDP2 = 0x%08x\n",__raw_readl(tmp_addr+0x90));
    _PDBA("CEN2 = 0x%08x\n",__raw_readl(tmp_addr+0x94));
    _PDBA("CFN2 = 0x%08x\n",__raw_readl(tmp_addr+0x98));
    _PDBA("CSSA2 = 0x%08x\n",__raw_readl(tmp_addr+0x9C));
    _PDBA("CDSA2 = 0x%08x\n",__raw_readl(tmp_addr+0xA0));
    _PDBA("CSEL2 = 0x%08x\n",__raw_readl(tmp_addr+0xA4));
    _PDBA("CSFL2 = 0x%08x\n",__raw_readl(tmp_addr+0xA8));
    _PDBA("CDEL2 = 0x%08x\n",__raw_readl(tmp_addr+0xAC));
    _PDBA("CDFL2 = 0x%08x\n",__raw_readl(tmp_addr+0xB0));
    _PDBA("CSAC2 = 0x%08x\n",__raw_readl(tmp_addr+0xB4));
    _PDBA("CDAC2 = 0x%08x\n",__raw_readl(tmp_addr+0xB8));
    _PDBA("CCEN2 = 0x%08x\n",__raw_readl(tmp_addr+0xBC));
    _PDBA("CCFN2 = 0x%08x\n",__raw_readl(tmp_addr+0xC0));
    _PDBA("COLOR2 = 0x%08x\n",__raw_readl(tmp_addr+0xC4));
    _PDBA("CDP2 = 0x%08x\n",__raw_readl(tmp_addr+0xD0));
    _PDBA("CNDP2 = 0x%08x\n",__raw_readl(tmp_addr+0xD4));
    _PDBA("CCDN2 = 0x%08x\n",__raw_readl(tmp_addr+0xD8));
*/

    ret = register_chrdev(p347_CHARDEV_NUM,p347_CHARDEV_NAME,&p347_fpga_fops);
    if (ret < 0) {
	//gpio_free(p347_GPIO_FPGA_CS);
	gpio_free(p347_GPIO_FPGA_IRQ);
	_PDB("ERROR: cannot register char device for p347_fpga\n");
	return ret;
    }

    padconf_addr = scm_base + PADCONF_FPGA_IRQ_ADDR;
    reg_val = __raw_readl(padconf_addr);
    _PDBA("read irq padconf 0x%p ret 0x%x\n",(void*)padconf_addr,reg_val);
    //-----------------------------------------------------------
    //TODO: maybe move to board description
    ret = spi_register_driver(&p347_spi_driver);
    _PDBA("spi_register_driver ret=%d\n",ret);
    //-----------------------------------------------------------

    //print_gpmc_padconf(scm_base);
    //print_clken_registers();

    for (i=0; i<p347_ADC_CHANNELS_CNT; i++)
	ret = _create_buffer((t_data_buffer*)(&tpi.ABUF[i]));
    //TODO: recalc buffer size for PACK16 mode
    tpi.ROT.ptr = kcalloc(4*p347_PUMPBUF_MAXRECORDS,sizeof(u16),GFP_KERNEL);
    spin_lock_init(&(tpi.ROT.lock));

    printk("FPGA_RESET=>1\n");
    gpio_set_value(p347_GPIO_FPGA_RESET,1);

    _PDB("p347_fpga_init end\n");
    return 0;
}
device_initcall(p347_fpga_init);

//==========================================================================

static void __exit p347_fpga_exit(void)
{
    int i;
    _PDB("p347_fpga_exit start\n");
/*
    if (tpi.dmt != NULL) {
	omap_dm_timer_free(tpi.dmt);
    }
*/
    for (i=0; i<p347_ADC_CHANNELS_CNT; i++)
	_delete_buffer((t_data_buffer*)(&tpi.ABUF[i]));
    kfree(tpi.ROT.ptr);
    
    //TODO: something from SPI is not cleared properly
    //ukitmp
    spi_unregister_driver(&p347_spi_driver);
    
    unregister_chrdev(p347_CHARDEV_NUM,p347_CHARDEV_NAME);
    
    omap_stop_dma(p347_fpga_info.dma_ch);
    //omap_disable_dma_irq(p347_fpga_info.dma_ch);
    omap_free_dma(p347_fpga_info.dma_ch);
    dma_free_coherent(NULL,RD_BURST_LEN*2,d_ret,p347_fpga_info.dma_rd_addr);
    free_irq(p347_fpga_info.irqnum,&p347_fpga_info);

    //gpio_free(p347_GPIO_FPGA_CS);
    gpio_free(p347_GPIO_FPGA_IRQ);
    gpmc_cs_free(p347_GPMC_FPGA_CS_READ);
#ifdef CS67_MODE
    gpio_free(p347_GPIO_OSCIRQ);
    gpio_free(p347_GPIO_OSCDMA);
#else
    gpmc_cs_free(p347_GPMC_FPGA_CS_WRITE);
#endif
    kfree(p347_fpga_info.spi_rx_buff);
    kfree(p347_fpga_info.spi_tx_buff);

    //platform_device_unregister(p347_fpga_info->pdev);
    _PDB("p347_fpga_exit end\n");
}

//==========================================================================
//==========================================================================
//==========================================================================
module_exit(p347_fpga_exit);
//==========================================================================
MODULE_AUTHOR("Konstantin Utkin");
MODULE_LICENSE("GPL");
//==========================================================================
